/**
  ******************************************************************************
  * @file    bsp_st7789_lcd.c
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   st7789液晶屏驱动
  ******************************************************************************
  */ 

#include "bsp_st7789v_lcd.h"

uint16_t LCD_X_LENGTH = TFT_COLUMN_NUMBER;
uint16_t LCD_Y_LENGTH = TFT_LINE_NUMBER;

static sFONT *LCD_Currentfonts = &Font12x24;  //英文字体
static uint16_t CurrentTextColor   = BLACK;//前景色
static uint16_t CurrentBackColor   = WHITE;//背景色

//向SPI总线传输一个8位数据
void SPI_WriteData(uint8_t Data)
{
    uint8_t i=0;
    for(i=8;i>0;i--)
    {
        if (Data & 0x80)
        {
            SPI_SDA_EN; // SDA=1; //输出数据
        }
        else
        {
            SPI_SDA_DIS;    //SDA=0;
        }
        SPI_SCL_DIS;// SCL=0;
        SPI_SCL_EN;// SCL=1;
        Data<<=1;
	}
}

// 从SPI总线读取一个8位数据
uint8_t SPI_ReadData(void)
{
    uint8_t i = 0;
    uint8_t Data = 0;
        
    for(i = 8; i > 0; i--)
    {
        Data <<= 1; // 左移一位，为下一位数据腾出空间
        
        // 根据描述：MCU在SCL上升沿读取数据
        SPI_SCL_DIS; // SCL=0
        SPI_SCL_EN; // SCL=1，产生上升沿
        
        // 在SCL上升沿后读取SDA引脚状态
        if(gpio_output_bit_get(SPI_SDA_PORT, SPI_SDA_PIN))
        {
            Data |= 0x01; // 如果SDA为高，设置最低位为1
        }        
    }
    
    return Data;
}

//向液晶屏写一个8位指令
void LCD_WriteIndex(uint8_t Data)
{
    SPI_CS_DIS;// CS=0;
    SPI_DC_DIS;// RS=0;
    SPI_WriteData(Data); 		
    SPI_CS_EN;// CS=1;
}

//向液晶屏写一个8位数据
void LCD_WriteData(uint8_t Data)
{	
    SPI_CS_DIS;// CS=0;
    SPI_DC_EN;// RS=1;
    SPI_WriteData(Data); 	
    SPI_CS_EN;// CS=1;
}
//向液晶屏写一个16位数据
void LCD_WriteData_16Bit(uint16_t Data)
{
    SPI_CS_DIS;// CS=0;
    SPI_DC_EN;// RS=1;
    SPI_WriteData(Data>>8); 	//写入高8位数据
    SPI_WriteData(Data); 			//写入低8位数据
    SPI_CS_EN;// CS=1;
}

void LCD_Reset()
{
    SPI_RST_DIS;// reset=0;
    delay_1ms(10);//100
    SPI_RST_EN;// reset=1;
    delay_1ms(10);//100
}

/**
 * @brief 液晶屏初始化 
 * @note 显示屏相关GPIO已在bsp_gpio里完成初始化
*/
void LCD_Initial(void)
{	
    // 硬件复位
    LCD_Reset();
    delay_1ms(120);  // 延时120ms

    // 退出睡眠模式
    LCD_WriteIndex(0x11);
    delay_1ms(120);

    // 内存访问控制设置
    LCD_WriteIndex(0x36);
    #ifdef DISPLAY_ROTATION_0
        LCD_WriteData(0x00);  // 正常方向
    #elif defined DISPLAY_ROTATION_90
        LCD_WriteData(0x60);  // 旋转90度
    #elif defined DISPLAY_ROTATION_180
        LCD_WriteData(0xC0);  // 旋转180度
    #elif defined DISPLAY_ROTATION_270
        LCD_WriteData(0xA0);  // 旋转270度
    #else
        LCD_WriteData(0x00);  // 默认正常方向
    #endif

    // 接口像素格式：16位RGB
    LCD_WriteIndex(0x3A);
    LCD_WriteData(0x05);

    //  porch设置
    LCD_WriteIndex(0xB2);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x00);
    LCD_WriteData(0x33);
    LCD_WriteData(0x33);

    //  gate控制
    LCD_WriteIndex(0xB7);
    LCD_WriteData(0x35);

    // VCOM设置
    LCD_WriteIndex(0xBB);
    LCD_WriteData(0x19);  // VCOM = 0.725V

    // LCM控制
    LCD_WriteIndex(0xC0);
    LCD_WriteData(0x2C);

    // VDV和VRH命令使能
    LCD_WriteIndex(0xC2);
    LCD_WriteData(0x01);

    // VRH设置
    LCD_WriteIndex(0xC3);
    LCD_WriteData(0x12);

    // VDV设置
    LCD_WriteIndex(0xC4);
    LCD_WriteData(0x20);

    // 帧率控制（正常模式）
    LCD_WriteIndex(0xC6);
    LCD_WriteData(0x0F);

    // 电源控制1
    LCD_WriteIndex(0xD0);
    LCD_WriteData(0xA4);
    LCD_WriteData(0xA1);

    // 正电压gamma控制
    LCD_WriteIndex(0xE0);
    LCD_WriteData(0xD0);
    LCD_WriteData(0x04);
    LCD_WriteData(0x0D);
    LCD_WriteData(0x11);
    LCD_WriteData(0x13);
    LCD_WriteData(0x2B);
    LCD_WriteData(0x3F);
    LCD_WriteData(0x54);
    LCD_WriteData(0x4C);
    LCD_WriteData(0x18);
    LCD_WriteData(0x0D);
    LCD_WriteData(0x0B);
    LCD_WriteData(0x1F);
    LCD_WriteData(0x23);

    // 负电压gamma控制
    LCD_WriteIndex(0xE1);
    LCD_WriteData(0xD0);
    LCD_WriteData(0x04);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x11);
    LCD_WriteData(0x13);
    LCD_WriteData(0x2C);
    LCD_WriteData(0x3F);
    LCD_WriteData(0x44);
    LCD_WriteData(0x51);
    LCD_WriteData(0x2F);
    LCD_WriteData(0x1F);
    LCD_WriteData(0x1F);
    LCD_WriteData(0x20);
    LCD_WriteData(0x23);

    // 显示反转开启（可选，根据屏幕特性）
    LCD_WriteIndex(0x21);
    delay_1ms(10);

    // 睡眠模式退出
    LCD_WriteIndex(0x11);
    delay_1ms(120);

    // 设置显示区域（240x240）
    LCD_WriteIndex(0x2A);  // 列地址设置
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0xEF);   // 239 = 0xEF (240-1)

    LCD_WriteIndex(0x2B);  // 行地址设置
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteData(0xEF);   // 239 = 0xEF (240-1)

    // 开启显示
    LCD_WriteIndex(0x29);
    delay_1ms(25);
}

/**
  * @brief  设置英文字体类型
  * @param  fonts: 指定要选择的字体
	*		参数为以下值之一
  * 	@arg：Font24x32;
  * 	@arg：Font16x24;
  * 	@arg：Font8x16;
  * @retval None
  */
void LCD_SetFont(sFONT *fonts)
{
	LCD_Currentfonts = fonts;
}

/**
  * @brief  设置LCD的前景(字体)及背景颜色,RGB565
  * @param  TextColor: 指定前景(字体)颜色
  * @param  BackColor: 指定背景颜色
  * @retval None
  */
void LCD_SetColors(uint16_t TextColor, uint16_t BackColor) 
{
	CurrentTextColor = TextColor; 
	CurrentBackColor = BackColor;
}

/**
  * @brief  设置LCD的前景(字体)颜色,RGB565
  * @param  Color: 指定前景(字体)颜色 
  * @retval None
  */
void LCD_SetTextColor(uint16_t Color)
{
	CurrentTextColor = Color;
}

/**
  * @brief  设置LCD的背景颜色,RGB565
  * @param  Color: 指定背景颜色 
  * @retval None
  */
void LCD_SetBackColor(uint16_t Color)
{
	CurrentBackColor = Color;
}

/**
 * @brief  在显示器上开辟一个窗口
 * @param  usX ：在特定扫描方向下窗口的起点X坐标
 * @param  usY ：在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth ：窗口的宽度
 * @param  usHeight ：窗口的高度
 * @retval 无
 */
void LCD_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{	
	LCD_WriteIndex(0x2a); 				 /* 设置X坐标 */
	LCD_WriteData(usX >> 8);	 /* 先高8位，然后低8位 */
	LCD_WriteData(usX & 0xff);
	LCD_WriteData((usX + usWidth - 1) >> 8);
	LCD_WriteData((usX + usWidth - 1) & 0xff);

	LCD_WriteIndex(0x2b); 			     /* 设置Y坐标*/
	LCD_WriteData(usY >> 8);
	LCD_WriteData(usY & 0xff);
	LCD_WriteData((usY + usHeight - 1 ) >> 8);
	LCD_WriteData((usY + usHeight - 1) & 0xff);
    LCD_WriteIndex(0x2c);
}

/**
 * @brief  对显示器的某一窗口以某种颜色进行清屏
 * @param  usX ：在特定扫描方向下窗口的起点X坐标
 * @param  usY ：在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth ：窗口的宽度
 * @param  usHeight ：窗口的高度
 * @param  color ：窗口的颜色
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void LCD_FillColor(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight, uint16_t color)
{
    uint16_t i,j;
    LCD_OpenWindow(usX, usY, usWidth, usHeight);
    SPI_DC_EN;  // RS=1;
    for (i=0; i < usWidth; i++)
        for (j=0; j < usHeight; j++)
        {
            LCD_WriteData_16Bit(color);
        }
}

// /**
//  * @brief  在显示器上以某一颜色填充像素点
//  * @param  ulAmout_Point ：要填充颜色的像素点的总数目
//  * @param  usColor ：颜色
//  * @retval 无
//  */
// static void LCD_FillColor(uint32_t ulAmout_Point, uint16_t usColor)
// {
//     uint32_t i = 0;
    
//     /* memory write */
//     LCD_WriteIndex(0x2c);	
        
//     for (i = 0; i < ulAmout_Point; i++)
//         LCD_WriteData(usColor);
// }


/**
 * @brief  设定光标坐标
 * @param  usX ：在特定扫描方向下光标的X坐标
 * @param  usY ：在特定扫描方向下光标的Y坐标
 * @retval 无
 */
static void LCD_SetCursor (uint16_t usX, uint16_t usY)	
{
	//ILI9341_OpenWindow ( usX, usY, 1, 1 );
	LCD_WriteIndex(0x2a); 				 /* 设置X坐标 */
	LCD_WriteData(usX >> 8);	 /* 先高8位，然后低8位 */
	LCD_WriteData(usX & 0xff);	 /* 设置起始点和结束点*/

	LCD_WriteIndex(0x2b); 			     /* 设置Y坐标*/
	LCD_WriteData(usY >> 8);
	LCD_WriteData(usY & 0xff);
}

/**
 * @brief  对显示器的某一点以某种颜色进行填充
 * @param  usX ：在特定扫描方向下该点的X坐标
 * @param  usY ：在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void LCD_SetPointPixel(uint16_t usX, uint16_t usY)	
{	
	LCD_SetCursor(usX, usY);	
	LCD_WriteIndex(0x2c);
    LCD_WriteData(CurrentTextColor >> 8);
    LCD_WriteData(CurrentTextColor & 0xFF);
}

// 读取ST7789像素值
uint16_t LCD_ReadPixel(uint16_t x, uint16_t y)
{
    uint8_t high_byte, low_byte;
    // 设置要读取的像素位置
    LCD_OpenWindow(x, y, 1, 1);
    
    // 发送读取命令
    LCD_WriteIndex(0x2E); // 内存读取命令
    
    // 准备读取数据
    SPI_DC_EN; // DC=1表示数据
    SPI_CS_DIS; // CS=0使能芯片
    
    // 丢弃第一个字节（根据ST7789数据手册，读取时有dummy字节）
    SPI_ReadData();
    
    // 读取像素数据（RGB565格式，16位）
    high_byte = SPI_ReadData(); // 高字节
    low_byte = SPI_ReadData();  // 低字节
    
    SPI_CS_EN; // CS=1禁用芯片
    
    // 组合成16位像素值
    return (high_byte << 8) | low_byte;
}

/**
 * @brief  在显示器上使用 Bresenham 算法画线段 
 * @param  usX1 ：在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 ：在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 ：在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 ：在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void LCD_DrawLine(uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2)
{
    uint16_t us; 
    uint16_t usX_Current, usY_Current;
    
    int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance; 
    int32_t lIncrease_X, lIncrease_Y; 	
    
    LCD_OpenWindow(usX1, usY1, usX2 - usX1, 1);	
    lDelta_X = usX2 - usX1; //计算坐标增量 
    lDelta_Y = usY2 - usY1; 
    
    usX_Current = usX1; 
    usY_Current = usY1; 
        
    if ( lDelta_X > 0 ) 
        lIncrease_X = 1; //设置单步方向 
    
    else if ( lDelta_X == 0 ) 
        lIncrease_X = 0;//垂直线 
    
    else 
    { 
        lIncrease_X = -1;
        lDelta_X = - lDelta_X;
    } 

    if ( lDelta_Y > 0 )
        lIncrease_Y = 1; 
    
    else if ( lDelta_Y == 0 )
        lIncrease_Y = 0;//水平线 
    
    else 
    {
        lIncrease_Y = -1;
        lDelta_Y = - lDelta_Y;
    } 

    if (  lDelta_X > lDelta_Y )
        lDistance = lDelta_X; //选取基本增量坐标轴 
    
    else 
        lDistance = lDelta_Y; 

    for ( us = 0; us <= lDistance + 1; us ++ )//画线输出 
    {  
        LCD_SetPointPixel ( usX_Current, usY_Current );//画点 
        
        lError_X += lDelta_X ; 
        lError_Y += lDelta_Y ; 
        
        if ( lError_X > lDistance ) 
        { 
            lError_X -= lDistance; 
            usX_Current += lIncrease_X; 
        }
        
        if ( lError_Y > lDistance ) 
        { 
            lError_Y -= lDistance; 
            usY_Current += lIncrease_Y; 
        }	
    }
}   

/**
 * @brief  在显示器上画一个矩形
 * @param  usX_Start ：在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start ：在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth：矩形的宽度（单位：像素）
 * @param  usHeight：矩形的高度（单位：像素）
 * @param  ucFilled ：选择是否填充该矩形
  *   该参数为以下值之一：
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形 
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void LCD_DrawRectangle(uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled)
{
	if ( ucFilled )
	{
		LCD_FillColor (usX_Start, usY_Start, usWidth, usHeight, CurrentTextColor);	
	}
	else
	{
		LCD_DrawLine(usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start);
		LCD_DrawLine(usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1);
		LCD_DrawLine(usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1);
		LCD_DrawLine(usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1);		
	}
}

/**
 * @brief  在显示器上显示一个英文字符
 * @param  usX ：在特定扫描方向下字符的起始X坐标
 * @param  usY ：在特定扫描方向下该点的起始Y坐标
 * @param  cChar ：要显示的英文字符
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void LCD_DispChar_EN ( uint16_t usX, uint16_t usY, const char cChar )
{
    uint8_t  byteCount, bitCount,fontLength;	
    uint16_t ucRelativePositon;
    uint8_t *Pfont;
    
    //对ascii码表偏移（字模表不包含ASCII表的前32个非图形符号）
    ucRelativePositon = cChar - ' ';
    
    //每个字模的字节数
    fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8;
        
    //字模首地址
    /*ascii码表偏移值乘以每个字模的字节数，求出字模的偏移位置*/
    Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength];
    
    //设置显示窗口
    LCD_OpenWindow ( usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height);

    //按字节读取字模数据
    //由于前面直接设置了显示窗口，显示数据会自动换行
    for ( byteCount = 0; byteCount < fontLength; byteCount++ )
    {
        //一位一位处理要显示的颜色
        for ( bitCount = 0; bitCount < 8; bitCount++ )
        {
            if ( Pfont[byteCount] & (0x80>>bitCount) )
            {
                LCD_WriteData_16Bit( CurrentTextColor );			
            }
            else
            {
                LCD_WriteData_16Bit( CurrentBackColor );
            }
        }	
    }
    LCD_OpenWindow(0, 0, LCD_X_LENGTH, LCD_Y_LENGTH);
}

// 优化后的显示透明背景字符函数
void LCD_DrawCharNoBack(uint16_t x, uint16_t y, char ch) 
{
    // 获取字体信息
    sFONT *font = LCD_Currentfonts;
    uint8_t bytesPerRow = (font->Width + 7) / 8; // 每行字节数
    uint16_t charOffset = (ch - ' ') * font->Height * bytesPerRow; // 字符在字库中的偏移
    
    // 遍历字符的每一行
    for (uint16_t row = 0; row < font->Height; row++) 
    {
        uint16_t pixelY = y + row;
        
        // 检查Y坐标是否在屏幕范围内
        if (pixelY >= LCD_Y_LENGTH) break;
        
        // 遍历当前行的每个字节
        for (uint8_t col = 0; col < bytesPerRow; col++) 
        {
            uint8_t byteData = font->table[charOffset + row * bytesPerRow + col];
            
            // 处理当前字节的8个位
            for (uint8_t bit = 0; bit < 8; bit++) 
            {
                uint16_t pixelX = x + col * 8 + bit;
                
                // 检查X坐标是否超出字符宽度或屏幕范围
                if (pixelX >= x + font->Width || pixelX >= LCD_X_LENGTH) 
                    continue;
                
                // 如果位被设置，则绘制像素
                if (byteData & (0x80 >> bit)) 
                {
                    LCD_SetPointPixel(pixelX, pixelY);
                }
            }
        }
    }
}

// 优化后的显示透明背景字符串函数
void LCD_DrawString_EN(uint16_t x, uint16_t y, const char *str) 
{
    sFONT *font = LCD_Currentfonts;
    uint16_t currentX = x;
    uint16_t currentY = y;
    
    while (*str != '\0') 
    {
        // 处理换行
        if (*str == '\n') 
        {
            currentX = x;
            currentY += font->Height;
            str++;
            continue;
        }
        
        // 检查是否需要换行（水平方向）
        if (currentX + font->Width > LCD_X_LENGTH) 
        {
            currentX = TFT_X_START;
            currentY += font->Height;
        }
        
        // 检查是否需要回到顶部（垂直方向）
        if (currentY + font->Height > LCD_Y_LENGTH) 
        {
            currentX = TFT_X_START;
            currentY = TFT_Y_START;
        }
        
        // 绘制字符
        LCD_DrawCharNoBack(currentX, currentY, *str);
        
        // 移动到下一个字符位置
        currentX += font->Width;
        str++;
    }
}

// 绘制基础矩形进度条
// x, y: 进度条左上角坐标
// width, height: 进度条尺寸
// value: 进度值 (0-100)
// borderColor: 边框颜色（可选）
void LCD_DrawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t value)
{
    // 确保进度值在0-100范围内
    if (value > 100) value = 100;
    
    // 计算进度宽度
    uint16_t progressWidth = (width * value) / 100;
    
    // 绘制背景
    LCD_FillColor(x, y, width, height, CurrentBackColor);
    
    // 绘制进度
    if (progressWidth > 0) {
        LCD_FillColor(x, y, progressWidth, height, CurrentTextColor);
    }
    
    // 绘制边框
    // if (borderColor != CurrentBackColor) {
        // LCD_DrawRectangle(x, y, width, height, borderColor, false);
    // }
}

